package com.markhsiu.mysql.packet.client;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;

import com.markhsiu.mysql.packet.BufferUtil;
import com.markhsiu.mysql.packet.Capabilities;
import com.markhsiu.mysql.packet.MysqlPacket;
import com.markhsiu.mysql.packet.PacketWritable;
import com.markhsiu.mysql.packet.SecurityUtil;
import com.markhsiu.mysql.packet.server.HandshakePacket;
import com.markhsiu.mysql.session.Session;

/**
 * 鉴权消息，客户端向服务端发起  
 * @author Administrator
 *
 */
public class AuthPacket extends MysqlPacket  implements PacketWritable{
	
	/**
	 * 客户端权能标志：用于与客户端协商通讯方式， 标志位含义与握手初始化报文中的相同。客户端收到服务器发来的初始化报文后，
	 * 会对服务器发送的权能标志进行修改，保留自身所支持的功能，然后将权能标返回给服务器， 从而保证服务器与客户端通讯的兼容性。
	 * 
	 */
	public int clientFlag;

	/**
	 * 最大消息长度：客户端发送请求报文时所支持的最大消息长度值。一般为16M
	 * 
	 */
	public int maxPackageLong;

	/**
	 * 字符编码：标识通讯过程中使用的字符编码，与服务器在认证初始化报文中发送的相同。
	 * 
	 */
	public byte charSetIndex;

	/**
	 * 填充值,一般长度23
	 */
	public byte[] fillbyte;

	/**
	 * 填充值值信息
	 * 
	 */
	public final byte[] FILLER = new byte[23];

	/**
	 * 用户名：客户端登陆用户的用户名称。
	 * 
	 */
	public byte[] userName;

	/**
	 * 挑战认证数据：客户端用户密码使用服务器发送的挑战随机数进行加密后， 生成挑战认证数据，然后返回给服务器，用于对用户身份的认证。
	 * 
	 */
	public byte[] password;

	/**
	 * 数据库名称：当客户端的权能标志位 CLIENT_CONNECT_WITH_DB 被置位时，该字段必须出现。
	 * 
	 */
	public byte[] dataBase;

	public HandshakePacket packet;
 
	public AuthPacket(HandshakePacket packet) {
		this.packet = packet;
	}

	@Override
	public void write(Session session) throws IOException {
		ByteBuffer writeBuffer = session.writeBuffer;
		length = -1;
		number = (byte)1;//第一个是握手包，第二个是鉴权包，第三个是OK包
		clientFlag = initClientFlags();
		maxPackageLong = PACKET_MAX;
		charSetIndex = (byte)0;
		fillbyte = FILLER;
		userName = session.connection.userName.getBytes();
		password = this.passwd(session.connection.passord,packet);
		dataBase = session.connection.dataBase.getBytes();
		
		length = getpackageSize();

		// 进行包大小的数据写入
		BufferUtil.writeUB3(writeBuffer, length);
		// 写入包在id
		writeBuffer.put(number);
		// 写入客户端标识
		BufferUtil.writeUB4(writeBuffer, clientFlag);
		// 写入最大包
		BufferUtil.writeUB4(writeBuffer, maxPackageLong);
		// 写入字符编码
		writeBuffer.put(charSetIndex);
		// 写入填充值
		writeBuffer.put(fillbyte);
		// 写入用户名
		if (null == userName) {
			writeBuffer.put((byte) 0);
		} else {
			BufferUtil.writeWithNull(writeBuffer, userName);
		}

		// 写入密码
		if (null == password) {
			writeBuffer.put((byte) 0);
		} else {
			BufferUtil.writeWithLength(writeBuffer, password);
		}

		// 写入dataBase
		if (null == dataBase) {
			writeBuffer.put((byte) 0);
		} else {
			BufferUtil.writeWithNull(writeBuffer, dataBase);
		}
		writeBuffer.flip();
 
	}



	@Override
	public String descrption() {
		return "";
	}

	/**
	 * 进行客户端的权能标识 方法描述
	 * 
	 * @return
	 * @创建日期 2016年12月13日
	 */
	private static int initClientFlags() {
		int flag = 0;
		flag |= Capabilities.CLIENT_LONG_PASSWORD;
		flag |= Capabilities.CLIENT_FOUND_ROWS;
		flag |= Capabilities.CLIENT_LONG_FLAG;
		flag |= Capabilities.CLIENT_CONNECT_WITH_DB;
		// flag |= Capabilities.CLIENT_NO_SCHEMA;
		boolean usingCompress = false;
		if (usingCompress) {
			flag |= Capabilities.CLIENT_COMPRESS;
		}
		flag |= Capabilities.CLIENT_ODBC;
		flag |= Capabilities.CLIENT_LOCAL_FILES;
		flag |= Capabilities.CLIENT_IGNORE_SPACE;
		flag |= Capabilities.CLIENT_PROTOCOL_41;
		flag |= Capabilities.CLIENT_INTERACTIVE;
		// flag |= Capabilities.CLIENT_SSL;
		flag |= Capabilities.CLIENT_IGNORE_SIGPIPE;
		flag |= Capabilities.CLIENT_TRANSACTIONS;
		// flag |= Capabilities.CLIENT_RESERVED;
		flag |= Capabilities.CLIENT_SECURE_CONNECTION;
		// client extension
		flag |= Capabilities.CLIENT_MULTI_STATEMENTS;
		flag |= Capabilities.CLIENT_MULTI_RESULTS;
		return flag;
	}
	

	/**
	 * 进行密码的组装
	 * 
	 * @param pass
	 *            密码信息
	 * @param hs
	 *            连接信息
	 * @return 密码信息
	 * @throws IOException
	 */
	private byte[] passwd(String pass, HandshakePacket hs) throws IOException {
		if (pass == null || pass.length() == 0) {
			return null;
		}
		byte[] passwd = pass.getBytes();
		int sl1 = hs.challengeRandom.length;
		int sl2 = hs.restOfScrambleBuff.length;
		byte[] seed = new byte[sl1 + sl2];
		System.arraycopy(hs.challengeRandom, 0, seed, 0, sl1);
		System.arraycopy(hs.restOfScrambleBuff, 0, seed, sl1, sl2);
		try {
			return SecurityUtil.scramble411(passwd, seed);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			throw new IOException(e);
		}
	}
	
	public int getpackageSize() {
 

		int size = 0;
		// 添加，客户端标识
		size = size + 4;
		// 包大小
		size = size + 4;
		// 1位字符编码
		size = size + 1;
		// 填充值
		size = size + 23;
		// 用户名
		size = size + ((userName == null) ? 1 : userName.length + 1);
		// 添加密码
		size = size + ((password == null) ? 1 : BufferUtil.getLength(password));
		// 数据库长度
		size = size + ((dataBase == null) ? 1 : dataBase.length + 1);
		return size;
	}
}
